//
// Created by xsh on 2025/6/4.
//

#include "MidiMessage.h"
#include <stdexcept>

MidiMessage::MidiMessage(const std::vector<uint8_t>& data, uint64_t timestamp)
        : mData(data), mTimestamp(timestamp) {
    if (data.empty()) {
        throw std::invalid_argument("MIDI message data cannot be empty");
    }
}

MidiMessage::MidiMessage(const uint8_t* data, size_t length, uint64_t timestamp)
        : mData(data, data + length), mTimestamp(timestamp) {
    if (length == 0) {
        throw std::invalid_argument("MIDI message data cannot be empty");
    }
}

const std::vector<uint8_t>& MidiMessage::getData() const {
    return mData;
}

uint64_t MidiMessage::getTimestamp() const {
    return mTimestamp;
}

uint8_t MidiMessage::getStatusByte() const {
    return mData[0];
}

uint8_t MidiMessage::getChannel() const {
    return mData[0] & 0x0F;
}

uint8_t MidiMessage::getPitch() const {
    if (mData.size() < 2) throw std::out_of_range("MIDI message too short for pitch");
    return mData[1];
}

uint8_t MidiMessage::getVelocity() const {
    if (mData.size() < 3) throw std::out_of_range("MIDI message too short for velocity");
    return mData[2];
}

bool MidiMessage::isNoteOn() const {
    return (mData[0] & 0xF0) == NOTE_ON;
}

bool MidiMessage::isNoteOff() const {
    return ((mData[0] & 0xF0) == NOTE_OFF) || (isNoteOn() && getVelocity() == 0);
}

bool MidiMessage::isControlChange() const {
    return (mData[0] & 0xF0) == CONTROL_CHANGE;
}

bool MidiMessage::isProgramChange() const {
    return (mData[0] & 0xF0) == PROGRAM_CHANGE;
}

bool MidiMessage::isPitchBend() const {
    return (mData[0] & 0xF0) == PITCH_BEND;
}